﻿#include "CEntity.h"

#include "../Utils/Utils.h"
#include "../Memory/GameData.h"

C_InventoryTransactionManager *C_Entity::getTransactionManager() const {
	return reinterpret_cast<C_InventoryTransactionManager *>(reinterpret_cast<uintptr_t>(this) + 0x1210);
}

C_PlayerInventoryProxy *C_Player::getSupplies() const {
	return *reinterpret_cast<C_PlayerInventoryProxy **>(reinterpret_cast<uintptr_t>(this) + 0xB70);
}

void C_LocalPlayer::unlockAchievements() {  // MinecraftEventing::fireEventAwardAchievement
	using fireEventAward = void(__fastcall *)(void *, int);
	static fireEventAward fireEventAwardFunc = reinterpret_cast<fireEventAward>(Utils::Utils::getBase() + 23747600);
	for (int i = 0; i < 118; i++)
		fireEventAwardFunc(this, i);
}

void C_LocalPlayer::applyTurnDelta(vec2_t &viewAngleDelta) {
	using applyTurnDelta = void(__thiscall *)(void *, vec2_t *);
	static applyTurnDelta TurnDelta = reinterpret_cast<applyTurnDelta>(Utils::Utils::getBase() + 14233472);
	TurnDelta(this, &viewAngleDelta);
}

void C_LocalPlayer::setGameModeType(int gma) {
	this->setC_PlayerGameType(gma);
}

void Level::playSound(std::u8string_view sound, vec3_t const &position, float volume, float pitch) {
	using playSound_t = void(__fastcall *)(Level *, TextHolder *, vec3_t const &, float, float);
	static playSound_t func = reinterpret_cast<playSound_t>(Utils::Utils::getBase() + 0x2447650);  // 48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 50 48 ?? ?? ?? ?? 00 00 33 FF 48 2B ?? ?? ?? 00 00 49 8B F0 48 C1 F8 03 48 8B EA (0x2447650)

	TextHolder th(sound);
	func(this, &th, position, volume, pitch);
}

fast_io::vector<C_Entity *> Level::getMiscEntityList() {
	using entityList_t = int64_t *(__fastcall *)(Level *, void *);
	static entityList_t func = reinterpret_cast<entityList_t>(Utils::Utils::getBase() + 0x2451510);  // 48 89 5C 24 ?? 56 57 41 56 48 83 EC 40 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 44 24 ?? 48 8B F2 4C 8B F1 48 89 54 24 ?? 33 C9

	char alloc[0x18];  // Allocate memory for the list
	int64_t *listStart = func(this, alloc);
	int listSize = static_cast<int>(((*reinterpret_cast<int64_t *>(reinterpret_cast<uintptr_t>(listStart) + 0x8)) - (*listStart)) >> 3);
	C_Entity **entityList = reinterpret_cast<C_Entity **>(*listStart);
	fast_io::vector<C_Entity *> res;
	if (listSize > 0 && listSize < 5000) [[likely]] {
		res.reserve(listSize);
		for (size_t i = 0; i < listSize; i++)
			res.emplace_back_unchecked(entityList[i]);
	}
	return res;
}

float C_Entity::getBlocksPerSecond() {
	static auto timer = GameData::getClientInstance()->minecraft->timer;
	return getTicksPerSecond() * *timer;
}

void C_Entity::lerpTo(vec3_t const &pos, vec2_t const &a2, int a3) {  // lerpTo was removed from the Player vtable so this is how we are going to use it from now on
	using lerpTo = void(__fastcall *)(C_Entity *, vec3_t const &, vec2_t const &, int);
	static lerpTo lerp = reinterpret_cast<lerpTo>(Utils::Utils::getBase() + 32726976);
	lerp(this, pos, a2, a3);
}

bool Level::hasEntity() const {
	return rayHitType == 1;
}

C_Entity *Level::getEntity() const {
	if (rayHitType != 1) return nullptr;
	C_Entity *retval = nullptr;
	GameData::forEachEntity([this, &retval](C_Entity *ent) {
		if (*(uint64_t *)((uintptr_t)ent + 0x10) == GamingEntityFinder) [[unlikely]] {
			retval = ent;
			return;
		}
	});
	return retval;
}

void C_Player::enchantItem(C_ItemStack &item, int id, int level) {
	using getEnchantsFromUserData_t = void(__fastcall *)(C_ItemStack *, void *);
	using addEnchant_t = bool(__fastcall *)(void *, __int64);
	using saveEnchantsToUserData_t = void(__fastcall *)(C_ItemStack *, void *);
	static getEnchantsFromUserData_t getEnchantsFromUserData = reinterpret_cast<getEnchantsFromUserData_t>(Utils::Utils::getBase() + 37525696);
	static addEnchant_t addEnchant = reinterpret_cast<addEnchant_t>(Utils::Utils::getBase() + 42316800);
	static saveEnchantsToUserData_t saveEnchantsToUserData = reinterpret_cast<saveEnchantsToUserData_t>(Utils::Utils::getBase() + 37526208);
	constexpr uint8_t EnchantData[0x60]{};
	getEnchantsFromUserData(&item, (void *)EnchantData);
	int64_t enchantPair = ((int64_t)level << 32) | id;
	if (addEnchant((void *)EnchantData, enchantPair)) [[likely]]  // Upper 4 bytes = level, lower 4 bytes = enchant type
		saveEnchantsToUserData(&item, (void *)EnchantData);
}
